//获取完整题目列表的html网页
#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<mutex>
#include<cassert>
#include<fstream>
#include<algorithm>
#include"oj_model_mysql.hpp"
#include"oj_view.hpp"
#include"../comm/log.hpp"
#include"../comm/util.hpp"
#include"../comm/httplib.h"
#include<json/json.h>


namespace ns_control
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_model;
    using namespace ns_view;
    using namespace httplib;



    // Machine类：存储一台“编译服务主机”的信息（比如IP、端口、当前负载）
    class Machine
    {
    public:
        std::string ip;  // 编译服务的ip
        int port;        // 编译服务的端口
        uint64_t load;   // 编译服务的负载
        std::mutex *mtx; // mutex禁止拷贝，使用指针
    public:
        Machine():ip(""),port(0),load(0),mtx(nullptr)
        {}
        ~Machine()
        {}
    public:

        // 提升负载（比如新接一个编译任务，负载+1）
        void IncLoad()
        {
            if (mtx)mtx->lock();
            load++;
            if (mtx)mtx->unlock();
        }


        //减少主机负载
        void DecLoad()
        {
            if (mtx)mtx->lock();
            load--;
            if (mtx)mtx->unlock();
        }

        //重置负载（比如主机离线后重新上线，负载清0）
        void ResetLoad()
        {
            if (mtx)mtx->lock();
            load=0;
            if (mtx)mtx->unlock();
        }
        // 获取当前负载（读负载时也要加锁，防止读的时候正在改）
        uint64_t Load()
        {
            uint64_t _load =0;
            if (mtx)mtx->lock();
            _load=load;// 先把load存到局部变量，再解锁（避免锁持有时间太长）
            if (mtx)mtx->unlock();
            return _load;
        }
    };

    // 编译服务配置文件路径（存所有编译主机的IP:端口，比如“127.0.0.1:8081”）
    const std::string service_machine="./conf/service_machine.conf";







    // LoadBlance类：管理所有编译服务主机，选“最闲”的主机处理任务 ---负载均衡模块
    class LoadBlance
    {
    private:
        std::vector<Machine> machines; // 提供编译服务的所有的主机,每一台主机都有自己的下标，充当当前主机的id
        std::vector<int> online;       // 所有在线的主机id
        std::vector<int> offline;      // 所有离线的主机id
        std::mutex mtx;                //保证LoadBlance数据安全

    public:
        LoadBlance()
        {
            assert(LoadConf(service_machine));
            LOG(INFO)<<"加载"<<service_machine<<"成功！"<<"\n";
        }
        ~LoadBlance()
        {}
    public:
        // 加载编译主机配置文件（比如service_machine.conf里每行是“IP:端口”）
        bool LoadConf(const std::string &machine_conf)
        {
            std::ifstream in(machine_conf);
            if(!in.is_open())
            {
                LOG(FATAL)<<"加载"<<machine_conf<<"失败！"<<"\n";
                return false;
            } 
            std::string line;
            while(std::getline(in,line))
            {
                std::vector<std::string> tokens;
                StringUtil::SplitString(line,&tokens,":");
                if(tokens.size()!=2)
                {
                    LOG(WARNING)<<"切分"<<line<<"失败！"<<"\n";
                    continue;
                }
                Machine m;
                m.ip=tokens[0];
                m.port=atoi(tokens[1].c_str());
                m.load=0;
                m.mtx=new std::mutex();
                //假设三台主机
                //online=[0,1,2],manchines[主机1][主机2][主机3],下表对应关系
                online.push_back(machines.size());
                machines.push_back(m);
            }
            in.close();
            return true;
        }


        // 核心功能：选“负载最小”的在线主机（输出：主机ID和主机指针）
        bool SmartChoice(int *id,Machine **m)
        {
            //1. 使用选择好的主机（更新该主机的负载）
            //2. 需要可能离线该主机
            mtx.lock();// 加锁：多用户同时选主机，防止在线列表被改乱
            //负载均衡算法:
            //1. 随机数法+hash
            //2. 轮询+hash
            int online_num=online.size();
            if(online_num==0)
            {
                mtx.unlock();
                LOG(FATAL)<<"所有主机已经离线"<<"\n";
                return false;
            }
            // 先默认选第一台在线主机作为“当前最小负载主机”
            *id=online[0];// online[0] 是第一个在线主机在machines中的下标（比如0）
            *m=&machines[online[0]];// 通过这个下标，从machines中找到对应的主机（machines[0]），并获取它的地址
            uint64_t min_load=machines[online[0]].Load();
            //通过遍历的方式找到负载最小的机器
            for(int i=1;i<online_num;i++)
            {
                uint64_t curr_load=machines[online[i]].Load();
                if(min_load>curr_load)
                {
                    min_load=curr_load;
                    *id=online[i];
                    *m=&machines[online[i]];
                }
            }
            mtx.unlock();
            return true;
        }


        void OnlineMachine()
        {
            //当所有主机都离线的时候，我们统一上线
            mtx.lock();
            online.insert(online.end(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            LOG(INFO)<<"所有的主机又上线了"<<"\n";
        }


        void OfflineMachine(int which)
        {
            mtx.lock();
            for(auto iter=online.begin();iter!=online.end();iter++)
            {
                if(*iter==which)
                {
                    machines[which].ResetLoad();
                    offline.push_back(which);
                    online.erase(iter);// 从在线列表删除（iter是迭代器， erase后要break避免迭代器失效）
                    break;//erase 会导致迭代器失效，而 break 能让我们在失效后立刻停止使用这个迭代器，从而避免程序崩溃。
                //当执行 online.erase(iter) 时（比如删除 iter 指向的元素 1）：
                //容器会把被删除元素后面的所有元素 “往前挪”，填补删除后的空位。
                //例如 online 原本是 [0,1,2]，删除 1 后变成 [0,2]。
                //此时原来的 iter 指针仍然指向原来的位置（但这个位置的元素已经被删除或移动了），就像 “原来指向第 2 个位置，现在第 2 个位置的元素变了，指针却没更新”。
                }
            }
            mtx.unlock();
        }


        void ShowMachines()
        {
            mtx.lock();
            std::cout<<"当前在线主机列表:";
            for(auto& id: online)
            {
                std::cout<<id<<" ";
            }
            std::cout<<std::endl;
            std::cout<<"当前离线主机列表:";
            for(auto& id: offline)
            {
                std::cout<<id<<" ";
            }
            std::cout<<std::endl;
            mtx.unlock();
        }
    };







    //Control类：整合所有业务（获取题目、判题、登录注册）
    class Control
    {
    private:
        Model _model;
        View _view;
        LoadBlance _load_blance;

        struct SessionData 
        {
            std::string username;  // 关联的用户名
            time_t expire_time;    // 过期时间（秒级时间戳）
        };
        std::unordered_map<std::string, SessionData> _session_map;  // 内存Session表
        std::mutex _session_mtx;  // 保护Session表的线程锁（多用户并发安全）
        const int SESSION_EXPIRE = 7 * 24 * 3600;  // Session有效期：7天

        // 2. 生成唯一SessionID（用时间戳+随机数，避免重复）
        std::string GenerateSessionID() 
        {
            std::string raw = TimeUtil::GetTimeMs() + std::to_string(rand());
            return MD5(raw);  // 复用你现有项目的MD5函数（comm/util.hpp中的MD5）
    }
    public:
        Control()
        {}
        ~Control()
        {}
    public:
    // 所有主机重新上线（调用负载均衡的OnlineMachine）
        void RecoveryMachine()
        {
            _load_blance.OnlineMachine();
        }


        //根据题目数据构建网页
        bool AllQuestions(std::string *html)
        {
            bool ret=true;
            std::vector<struct Question> all;
            // 调用Model的GetAllQuestions：从MySQL查所有题目
            if(_model.GetAllQuestions(&all))
            {
                sort(all.begin(),all.end(),[](const struct Question& q1,const struct Question& q2)
                {
                    return q1.number<q2.number;
                });
                //获取题目信息成功,将所有题目数据构建成网页
                _view.AllExpandHtml(all,html);
            }
            else
            {
                *html="获取题目失败,形成题目列表失败";
                ret=false;
            }
            return ret;
        }


        bool Question(const std::string& number,std::string *html)
        {
            struct Question q;
            bool ret=true;
            if(_model.GetOneQuestion(number,&q))
            {
                //获取指定题目信息成功,将所有题目数据构建成网页
                _view.OneExpandHtml(q,html);

            }
            else
            {
                *html="指定题目: "+number +" 不存在!";
                ret=false;
            }
            return ret;
        }


        // id:100
        // code:
        // input:
        void Judge(const std::string &number,const std::string in_json,std::string *out_json)
        {
            //0. 根据题号直接拿到对应的题目细节
            struct Question q;
            _model.GetOneQuestion(number,&q);
            //1. in_json进行反序列化，得到题目的id,得到用户提交的源代码
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json,in_value);
            std::string code=in_value["code"].asCString();
            //2. 重新拼接用户代码+测试用例代码，形成新的代码
            Json::Value compile_value;
            compile_value["input"]=in_value["input"];
            compile_value["code"]=code+q.tail;
            compile_value["cpu_limit"]=q.cpu_limit;
            compile_value["mem_limit"]=q.mem_limit;
            //序列化
            Json::FastWriter writer;
            std::string compile_string=writer.write(compile_value); 
            //3. 选择负载最低的主机:一直选择，直到主机可用，否则就是全部挂掉
            while(true)
            {
                int id=0;
                Machine *m=nullptr;
                if(!_load_blance.SmartChoice(&id,&m))
                {
                    break;
                }
                LOG(INFO)<<"选择主机成功,主机id: "<<id<<" 详情: "<<m->ip<<":"<<m->port<<" 该机器负载: "<<m->Load()<<"\n";
                //4. 然后发起http请求得到结果
                //给选中的主机发HTTP请求（调用编译服务的/compile_and_run接口）
                Client cli(m->ip,m->port);// 创建HTTP客户端（cpp-httplib库）
                m->IncLoad();//负载++
                if(auto res=cli.Post("/compile_and_run",compile_string,"application/json;charset=utf-8"))
                {
                    //5. 将结果赋值给out_json
                    if(res->status==200)//http协议
                    {
                        *out_json=res->body;
                        m->DecLoad();
                        LOG(INFO)<<"请求编译和运行服务成功..."<<"\n";
                        break;
                    }
                    m->DecLoad();// 请求成功但状态码不是200，负载也-1
                }
                else
                {
                    LOG(ERROR)<<"当请求的主机失败,主机id: "<<id<<" 详情: "<<m->ip<<":"<<m->port<<" 该机器负载: "<<m->Load()<<"可能已经离线"<<"\n";
                    //m->DecLoad();这里有个坑
                    _load_blance.OfflineMachine(id);
                    _load_blance.ShowMachines();//仅仅为了测试
                }

            }
        }


        // 创建Session（用户登录成功后调用）
        std::string CreateSession(const std::string& username) {
            std::string session_id = GenerateSessionID();  // 生成唯一SessionID
            SessionData data;
            data.username = username;
            data.expire_time = time(nullptr) + SESSION_EXPIRE;  // 设置过期时间

            // 线程安全地插入Session表
            std::lock_guard<std::mutex> lock(_session_mtx);
            _session_map[session_id] = data;
            return session_id;  // 返回SessionID，用于设置Cookie
        }


        // 通过SessionID查询Session（验证登录状态时调用）
        bool GetSession(const std::string& session_id, std::string* username) 
        {
            std::lock_guard<std::mutex> lock(_session_mtx);
            auto iter = _session_map.find(session_id);
            if (iter == _session_map.end()) {
                return false;  // Session不存在
            }

            // 1. 先清理所有过期的Session（遍历一次）
            auto cleariter = _session_map.begin();
            while (cleariter != _session_map.end()) {
                if (time(nullptr) > cleariter->second.expire_time) {
                    cleariter = _session_map.erase(cleariter);  // 过期则删除
                }   
                else {
                    ++cleariter;
                }
            }

            // 检查Session是否过期
            if (time(nullptr) > iter->second.expire_time) {
                _session_map.erase(iter);  // 过期则删除
                return false;
            }

            // Session有效，返回用户名
            *username = iter->second.username;
            return true;
        }

        // 销毁Session（退出登录时调用）
        void DestroySession(const std::string& session_id) {
            std::lock_guard<std::mutex> lock(_session_mtx);
            _session_map.erase(session_id);
        }


        // 处理注册
        bool DoRegister(const std::string& username, const std::string& password) {
            if (username.empty() || password.empty()) {
                return false;
            }
            LOG(INFO) << "收到注册请求: username=" << username << ", password=" << password; // 确认参数是否正确
            ns_model::User user{username, password, ""};
            return _model.RegisterUser(user);
        }

        // 处理登录
        bool DoLogin(const std::string& username, const std::string& password,std::string*session_id) {
            if (username.empty() || password.empty() || !session_id) {
                return false;
            }
            if(_model.VerifyLogin(username, password))
            *session_id=CreateSession(username);
            else
            return false;
            return true;
        }

    };
}

